package processor

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/cheggaaa/pb/v3"
	log "github.com/sirupsen/logrus"
	"github.com/twang2218/herbs/common"
)

func Process(herbs common.Herbs, pres common.Prescriptions) {
	//	按照名称长度倒叙、拼音排序
	log.Info("按照药材名称长度（倒叙）、拼音（正序）排序")
	herbs.Sort()
	for i := 0; i < 10; i++ {
		log.Infof("    %.10s    (%.2d) %s", herbs[i].Name, len(herbs[i].Descriptions), herbs[i].Descriptions[0].Book)
	}

	if err := herbs.SaveToCSV("data/herbs.csv"); err != nil {
		log.Errorf("无法将CSV写入 'herbs.csv'。'%s'", err.Error())
	}

	//	按照方剂名称排序
	log.Info("按照方剂拼音（正序）排序")
	pres.Sort()
	for i := 0; i < 10; i++ {
		log.Infof("    %.10s    (%.2d) %s", pres[i].Name, len(pres[i].Descriptions), pres[i].Descriptions[0].Origin)
	}

	log.Info("处理解析方剂配方")
	bar := pb.StartNew(len(pres))
	for i1, p := range pres {
		bar.Increment()
		for i2, d := range p.Descriptions {
			d.RecipeProcessed = processRecipe(d.Recipe, herbs)
			p.Descriptions[i2] = d
		}
		pres[i1] = p
	}
	bar.Finish()

	if err := pres.SaveToCSV("data/prescriptions.csv"); err != nil {
		log.Errorf("无法将CSV写入 'prescriptions.csv'。'%s'", err.Error())
	}

	log.Info("获得出现在方剂的药材名称")
	usedHerbNames := map[string]bool{}
	for _, p := range pres {
		for _, d := range p.Descriptions {
			mas := strings.Split(d.RecipeProcessed, ",")
			for _, ma := range mas {
				ms := strings.Split(ma, "/")
				m := ms[0]
				usedHerbNames[m] = true
			}
		}
	}
	log.Infof("总共有 %d 种药材，出现在 %d 个方剂中的有 %d 种药材。", len(herbs), len(pres), len(usedHerbNames))

	log.Info("获得出现在方剂的药材")
	var usedHerbs common.Herbs
	for _, h := range herbs {
		if _, ok := usedHerbNames[h.Name]; ok {
			usedHerbs.Add(h)
		}
	}
	usedHerbs.Sort()

	log.Info("保存出现在方剂的药材")
	if err := usedHerbs.SaveToCSV("data/herbs-used.csv"); err != nil {
		log.Errorf("无法将CSV写入 'herbs.csv'。'%s'", err.Error())
	}
	if err := usedHerbs.SaveToJSON("data/herbs-used.json"); err != nil {
		log.Errorf("无法写入 'medicine-used.json'。'%s'", err.Error())
	}
}

var (
	reRecipeClean      = regexp.MustCompile(`(?:（[^）]+）|\[[^\]]+\]|[,、，。：；	 ①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳\s]+)`)
	reRecipeListAmount = regexp.MustCompile(`(?P<names>(?:(?:[^\d\s\.．g各克两钱分]+)[ ]+)+(?:(?:[^\d\sg各克两钱分]+)[ ]*))(?:各\s*(?:[\d\.．]+[-～至])?(?P<amount>(?:[一二三四五六七八九十百半]+[两钱分匕]+)+[半]?|(?:(?:[\d／OICgQ\?]+(?:[ \.．]+[\dO]*)?|(?:[一二三四五六七八九][十百]?)+|[一二三四五六七八九十半数两]+|若干)\s*(?:g|kg|ml|L|文|[大中小余]?(?:干克|[千印克毫升公斤两分角厘钱匕斛斗石锭定铢磅镑寸尺厘米长宽]+|[灯盏钟块勺匙杓碗滴瓶茶酒杯瓢瓤园汁]+[匕]?|[粒枚只颗块头瓣团丸匣料包口]|[株支枝束捆掬丛根棵茎穗辨叶朵片皮荚节张个介合幅副帖贴箔页圈屯盘]|[杖把握段条截对双挺梃份倍子])[许]?|虎口|具|方寸|脚子|[如]?(?:鸡子壳|鸡子黄|鸡子|皂[角子]*|杏仁|桃|豇豆|豌豆|大豆|巴豆|黑豆|豆|粟子|橡子|弹子|枣|中指节|指|手掌|掌)(?:许大|许|大)|弹丸|拳许|全者|和合|全张|字|撮|捻|针|兑|竹筒|筒子|筒|商|党|孔|尾|抄|顶|付|秤|瓦)[半两]*[ ]?)+[\d]?|(?:少许|小许|适量|若干|减半|[如半]?(?:鸡子壳|鸡子|皂[角子]*|杏仁|桃|豌豆|豇豆|巴豆|大豆|黑豆|粟子|橡子|弹子|枣|中指节|指|手掌|掌)(?:许大|许|大)[一团]*|大如指|全个|全张|两半|钱半|等分|不[拘以问计]多少|随意加入|不拘分两|不计斤两|不拘斤两|不定|不拘几个|随多少)))`)
	reRecipeList       = regexp.MustCompile(`(?P<name>[^\d\s\.．各]+)`)
	reRecipeHerbAmount = regexp.MustCompile(`(?P<name>[^\s\d\-～\.．各]+?)\s*(?:[\d\.．]+[-～至])?(?P<amount>(?:[一二三四五六七八九十百半]+[两钱分匕]+)+[半]?|(?:(?:[\d／OICgQ\?]+(?:[ \.．]+[\dO]*)?|(?:[一二三四五六七八九][十百]?)+|[一二三四五六七八九十半数两]+|若干)\s*(?:g|kg|ml|L|文|[大中小余]?(?:干克|台半|[千印克毫升公斤两分角厘钱匕斛斗石锭定铢磅镑寸尺厘米长宽]+|[灯盏钟块勺匙杓碗滴瓶茶酒杯瓢瓤园汁]+[匕]?|[粒枚只颗块瓣团丸匣料包口头]|[株支枝束捆掬丛根棵茎穗辨叶朵片皮荚节张个介合幅副帖贴箔薄片页面圈屯盘]+|[宛杖把握段条截对双挺梃份子倍])[许]?|虎口|具|方寸|脚子|[如]?(?:鸡子壳|鸡子黄|鸡子|皂[角子]*|杏仁|桃|豇豆|豌豆|大豆|小豆|巴豆|黑豆|豆|粟子|橡子|弹子|枣|中指节|指|手掌|掌)(?:许大|许|大)|弹丸|拳许|全者|和合|全张|字|撮|捻|针|兑|竹筒|筒子|筒|商|党|孔|尾|抄|顶|付|秤|瓦)[半两]*[ ]?)+[\d]?|(?:少许|小许|适量|若干|减半|[如半]?(?:鸡子壳|鸡子|皂[角子]*|杏仁|桃|豌豆|豇豆|巴豆|大豆|小豆|巴豆|黑豆|粟子|橡子|弹子|球子|枣|中指节|指|手掌|掌)(?:许大|许|大)[一团]*|大如指|全个|全张|两半|钱半|等分|不[拘以问计]多少|随意加入|不拘分两|不计斤两|不拘斤两|不定|不拘几个|随多少))`)
	reNumberDot        = regexp.MustCompile(`[\.． ]+`)
)

type HerbAmount struct {
	Name   string
	Amount string
}

type HerbAmounts []HerbAmount

func (ma HerbAmount) String() string {
	return fmt.Sprintf("%s/%s", ma.Name, ma.Amount)
}

func (mas HerbAmounts) String() string {
	s := []string{}
	for _, ma := range mas {
		s = append(s, fmt.Sprint(ma))
	}
	return strings.Join(s, ",")
}

func processRecipe(text string, herbs []common.Herb) string {
	has := []HerbAmount{}

	// log.Tracef("----=====[ 原始文本 ]=====----")
	// log.Trace(text)
	// log.Tracef("----=====[ 原始文本 ]=====----")

	//	将序号、标点换味空格
	text = reRecipeClean.ReplaceAllString(text, " ")
	// log.Tracef("%q", text)

	//	带单位的药材列表
	las := reRecipeListAmount.FindAllStringSubmatch(text, -1)
	for _, la := range las {
		l := strings.Split(strings.TrimSpace(la[1]), " ")
		a := strings.TrimSpace(la[2])
		for _, m := range l {
			if len(m) > 0 {
				has = append(has, HerbAmount{
					Name:   strings.TrimSpace(m),
					Amount: a,
				})
			}
		}
	}
	text = strings.TrimSpace(reRecipeListAmount.ReplaceAllString(text, ""))

	// log.Trace("[reRecipeListAmount]")
	// log.Tracef("%s", HerbAmounts(has))
	// log.Tracef("%q", text)

	//	药材、重量对
	mma := reRecipeHerbAmount.FindAllStringSubmatch(text, -1)
	for _, ma := range mma {
		m := strings.TrimSpace(ma[1])
		a := strings.TrimSpace(ma[2])
		if len(m) > 0 {
			has = append(has, HerbAmount{
				Name:   m,
				Amount: a,
			})
		}
	}
	text = strings.TrimSpace(reRecipeHerbAmount.ReplaceAllString(text, ""))

	// log.Trace("[reRecipeHerbAmount]")
	// log.Tracef("%s", HerbAmounts(has))
	// log.Tracef("%q", text)

	//	reRecipeList
	la := reRecipeList.FindAllStringSubmatch(text, -1)
	for _, l := range la {
		m := strings.TrimSpace(l[1])
		if len(m) > 0 {
			has = append(has, HerbAmount{
				Name:   m,
				Amount: "",
			})
		}
	}
	// text = strings.TrimSpace(reRecipeList.ReplaceAllString(text, ""))

	// log.Trace("[reRecipeList]")
	// log.Tracef("%s", HerbAmounts(has))
	// log.Tracef("%q", text)

	//	清理 Amount 数字
	for i, ma := range has {
		if len(ma.Amount) > 0 {
			//	／OICgQ
			// oldAmount := ma.Amount
			ma.Amount = reNumberDot.ReplaceAllString(ma.Amount, ".")
			ma.Amount = strings.ReplaceAll(ma.Amount, "／", "/")
			ma.Amount = strings.ReplaceAll(ma.Amount, "O", "0")
			ma.Amount = strings.ReplaceAll(ma.Amount, "C", "0")
			// ma.Amount = strings.ReplaceAll(ma.Amount, "g", "9")
			ma.Amount = strings.ReplaceAll(ma.Amount, "Q", "0")
			// log.Tracef("%s => %s", oldAmount, ma.Amount)
			has[i] = ma
		}
	}

	// log.Trace("[Amount]")
	// log.Tracef("%s", HerbAmounts(has))
	// log.Tracef("%q", text)

	//	过滤非法药材名称（只保存那些存在于药材列表的药材）
	has2 := []HerbAmount{}
	for _, ma := range has {
		for _, med := range herbs {
			if strings.Contains(ma.Name, med.Name) {
				ma.Name = med.Name
				has2 = append(has2, ma)
				break
			}
		}
	}

	// log.Trace("[校验药材名称]")
	// log.Tracef("%s", HerbAmounts(has2))
	// log.Tracef("%q", text)

	return HerbAmounts(has2).String()
}
